
#ifndef COMMON_HPP_INCLUDED
#define COMMON_HPP_INCLUDED

// GLEW
#include "GL/glew.h"

// SDL 1.3
#include "SDL.h"

// GLM
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "glm/gtc/quaternion.hpp"
#include "glm/gtx/transform.hpp"
#include "glm/gtx/transform2.hpp"
#include "glm/gtx/euler_angles.hpp"

// stbi
#include "stb_image.h"
#include "stb_image_write.h"

// OpenCTM
#include "openctm.h"

// AntTweakBar
#include "AntTweakBar.h"

// C++ stdlib
#include <string>
#include <vector>
#include <stdexcept>
#include <iostream>
#include <sstream>

// CUDA
#include "cuda.h"
#include "cudaGL.h"
#include "PTXIntrinsics.h"

// On Windows SDL defines main as SDL_main.
#if defined(__WIN32__) && defined(main)
#   undef main
#endif

/** Helper string macros. */
#define __OGL_STRINGIFY(_x) #_x
#define _OGL_STRINGIFY(_x) __OGL_STRINGIFY(_x)
#define GLSL_BEGIN(ver) "#version " _OGL_STRINGIFY(ver) "\n#line " _OGL_STRINGIFY(__LINE__) " /* " _OGL_STRINGIFY(__FILE__) " */ "
#define NL "\n"

/** Handles common window events. Returns true if event was processed. */
bool ProcessCommonEvent(const SDL_Event& evt);
bool ProcessGUIEvent(const SDL_Event& evt);

void LoadGLProgram(GLenum target, const char* str);
std::string GetGLProgramInfo(GLenum target);

CUmodule LoadPTXModule(const char* ptxCode);

/**
 * Handles OpenGL window.
 */
class Window {
public:
    Window(const char* title, int w, int h, int x = SDL_WINDOWPOS_UNDEFINED, int y = SDL_WINDOWPOS_UNDEFINED, Uint32 flags = 0);
    ~Window();

    /** Swaps OpenGL buffers. */
    void Swap();

    SDL_Window* Get() const { return mWindow; }
    int Width() const { return mWidth; }
    int Height() const { return mHeight; }

private:
    SDL_Window* mWindow;
    int mWidth, mHeight;
    int mFrameCount;
    Uint32 mLastTime, mLastSecond;
};

/**
 * Rotates 3D object based on user's input.
 */
class ArcBall {
public:
    ArcBall() : mWidth(0), mHeight(0) {}

    void Begin(int x, int y);
    void End();
    void Drag(int x, int y);
    void SetWindowSize(int width, int height);
    const glm::mat4 Rotation() const { return glm::mat4_cast(mQuatNow); }

private:
    glm::vec3 ScreenToVector(float screenX, float screenY);

    int mWidth, mHeight;
    bool mDragging;
    glm::quat mQuatNow, mQuatDown;
    glm::vec3 mVecNow, mVecDown;
};

#endif
